﻿/// -----------------------------------------------------------------
/// MVEContent.svc: Maintains user logon methods.
/// License: see http://mve.codeplex.com/license; All Rights follows the MS-PL
/// Current owner: shiniwa
/// The project decription: please refer to http://codeplex.com/mve/
/// -----------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlClient;

namespace MVEContentSvc
{
    /*
     * 日本SGI株式会社
     * ヨハンソン・ヘンリク
     * johansson@sgi.co.jp
     * 
     * Change Log:
     * ---------------------------------------------------------------------------
     * 2009.01.07	Johansson Henrik		Class Created.
     * 2008.01.08							Changed Access Modifier of _loggedInUsers 
     *										to Internal
     */

    /// <summary>
    /// This static class provides methods to verify user credentials
    /// in the Database, logging in and logging out users from the Service,
    /// and other operations that deal with users of the Service.
    /// Use the static methods in this class to perform  
    /// User related operations in the Service.
    /// </summary>
    /// <remarks>INSERT:Creds</remarks>
    internal static class UserManager
    {
        /// <summary>
        /// Defines how long the user's login is valid in the Service.
        /// </summary>
        public static double ValidLoginPeriodInHours { get; set; }

        /// <summary>
        /// Holds a list of all the currently logged in users.
        /// </summary>
        internal static Dictionary<Guid, UserDataObject> _loggedInUsers
            = new Dictionary<Guid, UserDataObject>();

        /// <summary>
        /// Verify a Username/Password pair agains the member table and get the 
        /// Database Id of the verified user.
        /// </summary>
        /// <remarks>Done</remarks>
        /// <param name="userName">Username to verify</param>
        /// <param name="password">Password to verify</param>
        /// <param name="userDbId">An Int to hold the Database Id of the verified user</param>
        /// <param name="connection">Sql Connection</param>
        /// <returns>True if the Credentials where verified</returns>
        public static bool VerifyUserCredentials(
            string userName, string password, ref int? userDbId, SqlConnection connection)
        {
            SqlCommand command = new SqlCommand();
            command.CommandTimeout = 60;
            bool result = false;

            try
            {
                command.Connection = connection;
                command.CommandType = System.Data.CommandType.StoredProcedure;
                command.CommandText = "VerifyUserCredentials";

                command.Parameters.Add("name", SqlDbType.VarChar, 32);
                command.Parameters.Add("password", SqlDbType.VarChar, 13);
                command.Parameters[0].Value = userName;
                command.Parameters[1].Value = password;

                SqlParameter pmId = new SqlParameter("databaseId", SqlDbType.Int);
                pmId.Direction = ParameterDirection.Output;
                command.Parameters.Insert(2, pmId);

                SqlParameter returnValue = new SqlParameter("@RETURN_VALUE", SqlDbType.Int);
                returnValue.Direction = ParameterDirection.ReturnValue;
                command.Parameters.Add(returnValue);

                command.ExecuteNonQuery();
                int resultCode = (int)returnValue.Value;

                if (resultCode == 0)
                {
                    userDbId = (int)pmId.Value;
                    result = true;
                }
                else
                    result = false;
            }
            catch (InvalidOperationException ex)
            {
                ErrorHandler.LogMessage(ex.Message, false);
                result = false;
            }
            catch (InvalidCastException ex)
            {
                ErrorHandler.LogMessage(ex.Message, false);
                result = false;
            }
            finally
            {
                command.Dispose();
            }

            return result;
        }//end:VerifyUserCredentials

        /// <summary>
        /// Gets the UserDbId associated with a UserServiceId.
        /// <remarks>Done</remarks>
        /// </summary>
        public static void GetUserDbIdFromUserServiceId(Guid userServiceId, ref int? userDbId)
        {
            try
            {
                userDbId = UserManager._loggedInUsers[userServiceId].userDbId;
                return;
            }
            catch (KeyNotFoundException ex)
            {
                ErrorHandler.LogMessage(ex.Message, false);
                return;
            }
        }//end:GetUserDbIdFromUserServiceId

        /// <summary>
        /// Verify that the UserServiceId is really logged in.
        /// </summary>
        /// <remarks>Done</remarks>
        /// <returns>True if the UserServiceId exists in the pool of logged in users.</returns>
        public static bool VerifyLoggedInUserRecord(Guid userServiceId)
        {
            try
            {
                //TODO:Determine default valid period
                if (UserManager.ValidLoginPeriodInHours == 0)
                    ValidLoginPeriodInHours = 12;

                if (UserManager._loggedInUsers.ContainsKey(userServiceId))
                {
                    DateTime loginTime = UserManager._loggedInUsers[userServiceId].userLogonTimestamp;
                    DateTime loginTimeExpires = loginTime.AddHours(UserManager.ValidLoginPeriodInHours);
                    if (loginTimeExpires.CompareTo(DateTime.Now) < 0)
                    {
                        //The session has expired
                        return false;
                    }
                    else
                        return true;
                }
                else
                    return false;

            }
            catch (NullReferenceException ex)
            {
                ErrorHandler.LogMessage(ex.Message, false);
                return false;
            }
        }//end:VerifyLoggedInUserRecord

        /// <summary>
        /// Register a logged in UserDbId to the pool of logged in users, and retrieve
        /// a UserServiceId for the UserDbId.
        /// </summary>
        /// <remarks>Done</remarks>
        /// <returns>A new UserServiceId associated with the provided UserDbId.</returns>
        public static Guid RegisterUserLogin(string userName, int userDbId)
        {
            UserDataObject user = new UserDataObject()
            {
                userDbId = userDbId,
                userName = userName,
                userLogonTimestamp = DateTime.Now
            };

            //Make really sure we are not trying to register the same
            //GUID for two different users.
            Guid t_UserID = Guid.NewGuid();
            int tries = 0;
            while (UserManager._loggedInUsers.ContainsKey(t_UserID))
            {
                //Prevent Never Ending Loop
                if (tries >= 100)
                    break;

                t_UserID = Guid.NewGuid();

                tries += 1;
            }

            user.userServiceId = t_UserID;

            UserManager._loggedInUsers.Add(user.userServiceId, user);

            return user.userServiceId;
        }//end:RegisterUserLogin

        /// <summary>
        /// Remove a UserServiceId from the pool of logged in users.
        /// </summary>
        /// <remarks>Done</remarks>
        /// <returns>True if the UserServiceId existed AND was removed from the pool.</returns>
        public static bool RegisterUserLogoff(Guid userServiceId)
        {
            if (UserManager._loggedInUsers.ContainsKey(userServiceId))
            {
                UserManager._loggedInUsers.Remove(userServiceId);
                return true;
            }
            else
                return false;
        }//end:RegisterUserLogoff


        /*
         * 日本SGI株式会社
         * ヨハンソン・ヘンリク
         * johansson@sgi.co.jp
         * 
         * Change Log:
         * ---------------------------------------------------------------------------
         * 2009.01.07	Johansson Henrik		Class Created.
         * 2008.01.08	Johansson Henrik		Changed Access Modifier to Internal
         */

        /// <summary>
        /// Represents a Service User object.
        /// </summary>
        internal class UserDataObject
        {
            public int userDbId { get; set; }
            public Guid userServiceId { get; set; }
            public string userName { get; set; }
            public DateTime userLogonTimestamp { get; set; }
        }
    }
}
